﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ShredTheLog.Common.MVVM;
using System.ComponentModel.Composition;
using ShredTheLog.Api;
using ShredTheLog.Api.DataContracts;
using System.Collections.ObjectModel;
using System.Windows.Input;
using ShredTheLog.Controls;
using ShredTheLog.Views;
using System.Diagnostics.Contracts;
using System.Windows.Threading;
using System.Windows;

namespace ShredTheLog.ViewModels
{
    public sealed class UserSearchViewModel : ViewModelBase, IDisposable
    {
        [Import]
        private IShredServices ShredServices { get; set; }

        [Import]
        private IConnectionSettingsSection ConnectionSettings { get; set; }

        [ContractInvariantMethod]
        void ObjectInvariant()
        {
            Contract.Invariant(PageIndex >= 0);
            Contract.Invariant(TotalUserCount >= 0);
        }

        public UserSearchViewModel()
        {
            Contract.Assume(PageIndex >= 0);
            Contract.Assume(TotalUserCount >= 0);
        }

        public int PageIndex 
        {
            get { return _pageIndex; }
            private set { _pageIndex = value; OnPropertyChanged(() => PageIndex); OnPropertyChanged(() => Users); } 
        }
        private int _pageIndex;

        public int PageCount 
        { 
            get 
            {
                if (_pages.Count != 0 && _pages[0].Length != 0)
                {
                    int remainder;
                    int quotient = Math.DivRem(TotalUserCount, _pages[0].Length, out remainder);
                    return remainder != 0 ? quotient + 1 : quotient;
                }
                else
                {
                    return 0;
                }
            }
        }

        public int TotalUserCount
        {
            get { return _totalUserCount; }
            private set { _totalUserCount = value; OnPropertyChanged(() => TotalUserCount); OnPropertyChanged(() => PageCount); }
        }
        private int _totalUserCount;

        public bool IsLoading 
        {
            get { return _isLoading; }
            set { _isLoading = value; OnPropertyChanged(() => IsLoading); }
        }
        private bool _isLoading;

        public IShortUserInfo[] Users { get { return _searchConditions != null && _pages.Count > PageIndex ? _pages[PageIndex] : null; } }

        private List<IShortUserInfo[]> _pages = new List<IShortUserInfo[]>();

        private UserSearchCommandParameter _searchConditions;

        private ICommand _showConditionsCommand;
        public ICommand ShowConditionsCommand
        {
            get
            {
                if (_showConditionsCommand == null)
                    _showConditionsCommand = new DelegateCommand(
                        () =>
                        {
                            var window = Application.Current.Windows.OfType<UserSearchConditionsView>().SingleOrDefault();
                            if (window == null)
                            {
                                Action<UserSearchCommandParameter> okAction =
                                    p =>
                                    {
                                        IsLoading = true;
                                        ConnectionSettings.GetClient().SearchUsers(p.KnownLanguage, p.PracticingLanguage, p.ChatsOnline, p.Country, p.MinAge, p.MaxAge, p.FirstName, p.LastName, p.Location, lastViewedUser:null).ObserveOn(ShredServices.Dispatcher).Finally(() => IsLoading = false).Subscribe(
                                            response =>
                                            {
                                                try
                                                {
                                                    _pages.Clear();
                                                    _pages.Add(response.Users);
                                                    TotalUserCount = response.RestCount;
                                                    _searchConditions = p;
                                                    PageIndex = 0;
                                                }
                                                catch (Exception ex)
                                                {
                                                    ShredServices.ShowError(ex);
                                                    IsLoading = false;
                                                }
                                            },
                                            ShredServices.ShowError);
                                    };
                                var conditionsViewModel = new UserSearchConditionsViewModel(okAction);
                                if (_searchConditions != null)
                                {
                                    conditionsViewModel.KnownLanguage = _searchConditions.KnownLanguage;
                                    conditionsViewModel.PracticingLanguage = _searchConditions.PracticingLanguage;
                                    conditionsViewModel.ChatsOnline = _searchConditions.ChatsOnline;
                                    conditionsViewModel.Country = _searchConditions.Country;
                                    conditionsViewModel.MinAge = _searchConditions.MinAge;
                                    conditionsViewModel.MaxAge = _searchConditions.MaxAge;
                                    conditionsViewModel.FirstName = _searchConditions.FirstName;
                                    conditionsViewModel.LastName = _searchConditions.LastName;
                                    conditionsViewModel.Location = _searchConditions.Location;
                                }
                                new UserSearchConditionsView().Show(conditionsViewModel);
                            }
                            else
                            {
                                window.Activate();
                            }
                        });
                return _showConditionsCommand;
            }
        }

        private ICommand _nextPageCommand;
        public ICommand NextPageCommand
        {
            get
            {
                if (_nextPageCommand == null)
                {
                    Action action =
                        () =>
                        {
                            Contract.Assume(_searchConditions != null);
                            Contract.Assume(_pages.Count > PageIndex);

                            if (_pages.Count > PageIndex + 1)
                            {
                                // Страница уже была загружена ранее, переходим на нее
                                PageIndex++;
                            }
                            else
                            {
                                // Страница еще не загружена, инициируем загрузку...
                                var whenPageDownloaded =
                                    from t in Observable.Return(new { PageIndex = PageIndex + 1, Request = _searchConditions.Clone(), LastViewedUser = _pages[PageIndex].LastOrDefault() })
                                    from response in ConnectionSettings.GetClient().SearchUsers(t.Request.KnownLanguage, t.Request.PracticingLanguage, t.Request.ChatsOnline, t.Request.Country, t.Request.MinAge, t.Request.MaxAge, t.Request.FirstName, t.Request.LastName, t.Request.Location, t.LastViewedUser)
                                    select new { PageIndex = t.PageIndex, Request = t.Request, Response = response };

                                IsLoading = true;
                                whenPageDownloaded.ObserveOn(ShredServices.Dispatcher).Finally(() => IsLoading = false).Subscribe(
                                    pageDownloadResult =>
                                    {
                                        try
                                        {
                                            if (_pages.Count == pageDownloadResult.PageIndex)
                                            {
                                                _pages.Add(pageDownloadResult.Response.Users);
                                                TotalUserCount = _pages.Take(pageDownloadResult.PageIndex).Sum(i => i.Length) + pageDownloadResult.Response.RestCount;
                                                _searchConditions = pageDownloadResult.Request;
                                                PageIndex = pageDownloadResult.PageIndex;
                                            }
                                        }
                                        catch (Exception ex)
                                        {
                                            ShredServices.ShowError(ex);
                                            IsLoading = false;
                                        }
                                    },
                                    ShredServices.ShowError);
                            }
                        };
                    _nextPageCommand = new DelegateCommand(action, () => _searchConditions != null && PageIndex < PageCount - 1);
                }
                return _nextPageCommand;
            }
        }

        private ICommand _previousPageCommand;
        public ICommand PreviousPageCommand
        {
            get
            {
                if (_previousPageCommand == null)
                {
                    Action action =
                        () =>
                        {
                            Contract.Assume(_searchConditions != null);
                            Contract.Assume(PageIndex > 0);

                            PageIndex--;
                        };
                    _previousPageCommand = new DelegateCommand(action, () => _searchConditions != null && PageIndex > 0);
                }
                return _previousPageCommand;
            }
        }

        private ICommand _closeCommand;
        public ICommand CloseCommand
        {
            get
            {
                if (_closeCommand == null)
                    _closeCommand = new DelegateCommand(Dispose);
                return _closeCommand;
            }
        }

        public void Dispose()
        {
            var searchConditionsWindow = Application.Current.Windows.OfType<UserSearchConditionsView>().SingleOrDefault();
            if (searchConditionsWindow != null)
                searchConditionsWindow.Close();
            if (Closed != null)
                Closed(this, EventArgs.Empty);
        }

        public event EventHandler Closed;
    }

    public sealed class UserSearchCommandParameter
    {
        public Language? KnownLanguage { get; private set; }
        public Language? PracticingLanguage { get; private set; }
        public bool? ChatsOnline { get; private set; }
        public Country? Country { get; private set; }
        public int? MinAge { get; private set; }
        public int? MaxAge { get; private set; }
        public string FirstName { get; private set; }
        public string LastName { get; private set; }
        public string Location { get; private set; }

        public UserSearchCommandParameter(Language? knownLanguage, Language? practicingLanguage, bool? chatsOnline, Country? country, int? minAge, int? maxAge, string firstName, string lastName, string location)
        {
            KnownLanguage = knownLanguage;
            PracticingLanguage = practicingLanguage;
            ChatsOnline = chatsOnline;
            Country = country;
            MinAge = minAge;
            MaxAge = maxAge;
            FirstName = firstName;
            LastName = lastName;
            Location = location;
        }

        public UserSearchCommandParameter Clone()
        {
            return new UserSearchCommandParameter(this.KnownLanguage, this.PracticingLanguage, this.ChatsOnline, this.Country, this.MinAge, this.MaxAge, this.FirstName, this.LastName, this.Location);
        }
    }
}
